﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;

namespace DotNet.ReflectionDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            /******************************************************************
            ** This is a simple model which we'll be using
            ******************************************************************/
            var car = new Car {Make = "Nissan", Model = "300zx", Year = 1992};


            /******************************************************************
            ** The first thing to learn about reflection is how to get property
            ** values from obects.
            ******************************************************************/
            Console.WriteLine("Getting values from an object through reflection");
            Console.WriteLine("Values for {0}:", car.GetType().Name);
            foreach (var prop in typeof(Car).GetProperties())
            {
                var name = prop.Name;
                var value = prop.GetValue(car, null);
                Console.WriteLine("   {0}: {1}", name, value);
            }

            /******************************************************************
            ** If you've come to the point where you're looking into relection
            ** you've probably come across attributes (or annotations). This 
            ** was my first introduction to using reflection. I always call the
            ** OfType<Attribute>() method rather than pass the type into the 
            ** GetCustomAttributes method. This also casts the attribute to the
            ** type you're looking for, alleviating the need for unboxing.
            ******************************************************************/
            Console.WriteLine("\n\nGetting attributes from properties through reflection");
            Console.WriteLine("Values for {0}:", car.GetType().Name);
            foreach (var prop in typeof(Car).GetProperties())
            {
                var attribute = prop.GetCustomAttributes(false).OfType<DisplayAttribute>().FirstOrDefault();
                var name = attribute != null ? attribute.Name : prop.Name;
                var value = prop.GetValue(car, null);
                Console.WriteLine("   {0}: {1}", name, value);
            }

            /******************************************************************
            ** The next cool thing we can do with reflection is call methods by
            ** name. I've never found a good reason to use this, but I'm sure
            ** someone else has. 
            ******************************************************************/
            Console.WriteLine("\n\nCalling a method from reflection");
            car.GetType().GetMethod("Start").Invoke(car, null);


            /******************************************************************
            ** Finally, I'll demonstrate how we can dynamically create an 
            ** instance of an object. Here I'll call a method, examine the 
            ** parameters and attempt to create an object of that type. Similar
             * methods can be used for constuctors as well.
            ******************************************************************/
            Console.WriteLine("\n\nCreating an instance and passing an argument ot a method called from reflection");
            var method = car.GetType().GetMethod("FillUp");
            var arguments = new List<object>();
            foreach (var parameter in method.GetParameters())
            {
                var constructor = parameter.ParameterType.GetConstructors().FirstOrDefault();
                if (constructor == null) continue;
                var instance = constructor.Invoke(null);
                arguments.Add(instance);
            }
            method.Invoke(car, arguments.ToArray());

            Console.ReadLine();
        }

        public class Car
        {
            [Display(Name = "Car Make")]
            public string Make { get; set; }

            [Display(Name = "Car Model")]
            public string Model { get; set; }

            [Display(Name = "Production Year")]
            public int Year { get; set; }

            public void Start()
            {
                Console.WriteLine("   The car has been started!");
            }

            public void FillUp(Gas gas)
            {
                Console.WriteLine("   Put {0} Gallons of Gas in the Tank", gas.Gallons);
            }
        }
    }

    public class Gas
    {
        public int Gallons { get { return 10; } }   
    }
}
